<!DOCTYPE html>
<html lang="zh-CN">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  
  
  <!-- 关键字 keywords -->
  
  <meta name="keywords" content="a,天赐,天赐の小站,博客">
  
  
  <title itemprop="name">RabbitMQ如何保证消息的可靠性传输 | 天赐の小站</title>
  
    <link rel="shortcut icon" href="//fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/favicon.ico">
  
  <meta http-equiv="x-dns-prefetch-control" content="on">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+SerifMerriweather|Merriweather+Sans|Source+Code+Pro|Ubuntu:400,700|Noto+Serif+SC" media="all">
  <link rel="dns-prefetch" href="//cdn.jsdelivr.net">
  <link rel="stylesheet" id="saukra_css-css" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/style.css" type="text/css" media="all">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/lib.min.css" media="all">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/font.css" media="all">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/insight.css" media="all">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/jquery.fancybox.min.css" media="all">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/zoom.css" media="all">
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/sharejs.css">
<!--   <link rel="stylesheet" id="saukra_css-css" href="https://2heng.xin/wp-content/cache/autoptimize/css/autoptimize_ad42a61f4c7d4bdd9f91afcff6b5dda5.css
" type="text/css" media="all"> -->
  <script>
  /*Initial Variables*/
  var mashiro_option = new Object();
  var mashiro_global = new Object();
  mashiro_option.NProgressON = true;
  /* 
   * 邮箱信息之类的东西可以填在这里，这些js变量基本都作用于sakura-app.js
   * 这样的设置仅是为了方便在基于PHP开发的主题中设置js变量，既然移植到了Node上，我想或许可以精简这一逻辑吧
   */
  mashiro_option.email_domain = "";
  mashiro_option.email_name = "";
  mashiro_option.cookie_version_control = "";
  mashiro_option.qzone_autocomplete = false;
  mashiro_option.site_name = "天赐の小站";
  mashiro_option.author_name = "天赐の小站";
  mashiro_option.site_url = "/";
  mashiro_option.v_appId = "VeenC2gYYyfSOq2rcCSmmruy-MdYXbMMI";
  mashiro_option.v_appKey = "F5kjyHdh4akqc7SDmfzQP8Qi";
  mashiro_option.v_notify = false;
  mashiro_option.v_verify = false;
  mashiro_option.v_placeholder = "你是我一生只会遇见一次的惊喜 ...";
  mashiro_option.v_avatar = "monsterid";
  mashiro_option.v_pageSize = "5" || 10;
  mashiro_option.mathjax = "0";
  mashiro_option.qq_api_url = "https://api.mashiro.top/qqinfo/"; 
  mashiro_option.qq_avatar_api_url = "https://api.mashiro.top/qqinfo/";

  // mashiro_option.jsdelivr_css_src = "https://cdn.jsdelivr.net/gh/moezx/cdn@3.4.5/css/lib.min.css";
  // mashiro_option.float_player_on = true;
  console.info("%c Blog %c", "background:#24272A; color:#ffffff", "", "https://blog.xiongtianci.com");
  console.info("%c GitHub %c", "background:#24272A; color:#ffffff", "", "https://github.com/tiancixiong");
  /*End of Initial Variables*/
  </script>
  <script type="text/javascript">
  var bg = "https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/5cm_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/5cm_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_05.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_06.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_07.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_08.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_09.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_10.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_11.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/A.Voyage.of.Chihiro_12.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Atonement_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Atonement_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Atonement_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Atonement_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Avatar_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Before.Sunrise_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Before.Sunrise_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Before.Sunset_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/bighero_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/bighero_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/bighero_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Coco_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Coco_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Coco_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/CUG.King.of.Heroes_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_05.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_06.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_07.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_08.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Digimon_09.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Friends_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Friends_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Friends_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Friends_04.webp,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Friends_05.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Harry.Potter_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Harry.Potter_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Harry.Potter_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Harry.Potter_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Harry.Potter_05.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Hauru.no.ugoku.shiro_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Into.the.Wild_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Kaze.tachinu_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiki_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiki_03.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiki_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiminona_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiminona_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiminona_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiminona_06.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/kiminona_07.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/koenokatachi_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/koenokatachi_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/koenokatachi_03.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Kotonoha.no.Niwa_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/La.La.Land_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Le.Petit.Prince_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Le.Petit.Prince_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mimi.wo.sumaseba_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mr.Bean.Holiday_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mr.Bean.Holiday_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mr.Bean.Holiday_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mr.Bean.Holiday_04.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Mr.Bean.Holiday_05.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/My.Sassy.Girl_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Paperman_01.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Paperman_02.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Piper_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Ponyo_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Ponyo_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Ponyo_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Reply.1988_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Reply.1988_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Reply.1988_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/SpiderMan.Into.the.SpiderVerse_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/SpiderMan.Into.the.SpiderVerse_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/SpiderMan.Into.the.SpiderVerse_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/The.Girl.Who.Leapt.Through.Time_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/The.Pianist_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/TheLonelyAndGreatGod_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Titanic_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Titanic_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Titanic_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/totoro1.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/WALL_E_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/WALL_E_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Zootopia_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/2049.Blade.Runner_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/2049.Blade.Runner_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/American.Dreams.in.China_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Biri.Girl_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Bohemian.Rhapsody_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Bohemian.Rhapsody_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Bohemian.Rhapsody_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Crouching.Tiger.Hidden.Dragon.2000_01.webp,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Detachment_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Detachment_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Gisaengchung_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Iron.Man_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Kureyon.Shinchan_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Love.Actually_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Roman.Holiday_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Roman.Holiday_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Sherlock.S01_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/The.Secret.Life.of.Walter.Mitty_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Youth.Over.Flowers.in.Africa_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Youth.Over.Flowers.in.Africa_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/My.Mister_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/One.Day_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/One.Day_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/One.Day_03.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Ready.Player.One_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Begin.Again_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Begin.Again_02.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/Freddie_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/naruto.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/SetoNaikai_01.jpg,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/yexiu_01.png,https://cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@230521/theme/cover/yexiu_02_FUJISKYTREE.png".split(",");
  var bgindex = Math.floor(Math.random()*bg.length);
  if (!!window.ActiveXObject || "ActiveXObject" in window) { //is IE?
    alert('朋友，IE浏览器未适配哦~');
  }
  </script>
  <style type="text/css">
  .hljs-ln{border-collapse:collapse}.hljs-ln td{padding:0}.hljs-ln-n:before{content:attr(data-line-number)}
  </style>
  <style type="text/css">.site-top .lower nav{display:block !important;}.author-profile i,.post-like a,.post-share .show-share,.sub-text,.we-info a,span.sitename,.post-more i:hover,#pagination a:hover,.post-content a:hover,.float-content i:hover{color:#FE9600}.feature i,.download,.navigator i:hover,.links ul li:before,.ar-time i,span.ar-circle,.object,.comment .comment-reply-link,.siren-checkbox-radio:checked + .siren-checkbox-radioInput:after{background:#FE9600}::-webkit-scrollbar-thumb{background:#FE9600}.download,.navigator i:hover,.link-title,.links ul li:hover,#pagination a:hover,.comment-respond input[type='submit']:hover{border-color:#FE9600}.entry-content a:hover,.site-info a:hover,.comment h4 a,#comments-navi a.prev,#comments-navi a.next,.comment h4 a:hover,.site-top ul li a:hover,.entry-title a:hover,#archives-temp h3,span.page-numbers.current,.sorry li a:hover,.site-title a:hover,i.iconfont.js-toggle-search.iconsearch:hover,.comment-respond input[type='submit']:hover{color:#FE9600}.comments .comments-main{display:block !important;}.comments .comments-hidden{display:none !important;}background-position:center center;background-attachment:inherit;}
  </style>
</head>
</html>
<body class="page-template page-template-user page-template-page-analytics page-template-userpage-analytics-php page page-id-1297 chinese-font serif isWebKit">
  <div class="scrollbar" id="bar">
  </div>
  <a href="#" class="cd-top faa-float animated"></a>
  <section id="main-container">
    <div class="headertop filter-dot">
  <!--首页背景图下方的波浪线-->
  <!--<div id="banner_wave_1"></div>-->
  <!--<div id="banner_wave_2"></div>-->
  <figure id="centerbg" class="centerbg">
    <div class="focusinfo no-select">
      <div class="header-tou">
        <a href="/">
          <img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/avatar.jpg">
        </a>
      </div>
      <div class="header-info">
        <p>You Only Live Once</p>
        <div class="top-social_v2">
          <li id="bg-pre">
            <img class="flipx" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/img/other/next-b.svg">
          </li>
          
            
              
                <li>
                  <a href="https://github.com/tiancixiong" target="_blank" class="social-github" title="github">
                    <img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/social/github.png">
                  </a>
                </li>
              
            
              
                <li>
                  <a href="mailto:support@xiongtianci.com" target="_blank" class="social-github" title="E-mail">
                    <img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/social/email.svg">
                  </a>
                </li>
              
            
          
          <li id="bg-next">
            <img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/img/other/next-b.svg">
          </li>
        </div>
      </div>
    </div>
  </figure>
  <div id="video-container" style="">
    <video style="object-fit: fill" id="bgvideo" class="video" video-name="" src="" width="auto" preload="auto">
    </video>
    <div id="video-btn" class="loadvideo videolive">
    </div>
    <div id="video-add">
    </div>
    <div class="video-stu">
    </div>
  </div>
  <div class="headertop-down faa-float animated" onclick="headertop_down()">
    <span>
      <i class="fa fa-chevron-down" aria-hidden="true">
      </i>
    </span>
  </div>
</div>
    <div id="page" class="site wrapper">
      <header class="site-header no-select gizle sabit" role="banner">
  <div class="site-top">
    <div class="site-branding">
      <span class="site-title">
        <span class="logolink moe-mashiro">
          <a href="/">
            <span class="sakurasono"></span>
            <span class="shironeko">天赐の小站</span>
          </a>
        </span>
      </span>
    </div>
    <div class="searchbox search-form-submit">
      <i class="iconfont js-toggle-search iconsearch icon-search">
      </i>
    </div>
    <div id="show-nav" class="showNav mobile-fit">
      <div class="line line1">
      </div>
      <div class="line line2">
      </div>
      <div class="line line3">
      </div>
    </div>
    <div class="lower-cantiner">
      <div class="lower">
        <nav class="mobile-fit-control hide">
          <ul id="menu-new" class="menu">
            
              <li>
                <a href="/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-fort-awesome faa-horizontal" aria-hidden="true"></i>
                    首页
                  </span>
                </a>
                
              </li>
            
              <li>
                <a href="/archives">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-terminal faa-vertical" aria-hidden="true"></i>
                    极客
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/categories/技术/">
                          <i class="fa fa-code" aria-hidden="true"></i>
                          技术
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/Linux/">
                          <i class="fa fa-linux" aria-hidden="true"></i>
                          Linux
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li>
                <a href="/archives">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-slack faa-spin" aria-hidden="true"></i>
                    爱好
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/categories/电影/">
                          <i class="fa fa-film" aria-hidden="true"></i>
                          电影
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/日语/">
                          <i class="fa fa-language" aria-hidden="true"></i>
                          日语
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li>
                <a href="/archives">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-archive faa-ring" aria-hidden="true"></i>
                    归档
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/categories/生活/">
                          <i class="fa fa-file-text-o" aria-hidden="true"></i>
                          生活
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/随想/">
                          <i class="fa fa-commenting-o" aria-hidden="true"></i>
                          随想
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/资源/">
                          <i class="fa fa-cloud-download" aria-hidden="true"></i>
                          资源
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/转载/">
                          <i class="fa fa-book" aria-hidden="true"></i>
                          转载
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li>
                <a href="/comment/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-pencil-square-o faa-tada" aria-hidden="true"></i>
                    留言板
                  </span>
                </a>
                
              </li>
            
              <li>
                <a href="/links/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-link faa-shake" aria-hidden="true"></i>
                    友人帐
                  </span>
                </a>
                
              </li>
            
              <li>
                <a href="/donate/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-heart faa-burst" aria-hidden="true"></i>
                    赞赏
                  </span>
                </a>
                
              </li>
            
              <li>
                <a href="/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-leaf faa-falling" aria-hidden="true"></i>
                    关于
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/about/">
                          <i class="fa fa-meetup" aria-hidden="true"></i>
                          我？
                        </a>
                      </li>
                    
                      <li>
                        <a href="/theme-sakura/">
                          <i class="fa iconfont icon-sakura" aria-hidden="true"></i>
                          主题
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li>
                <a href="/atom.xml">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-rss faa-passing" aria-hidden="true"></i>
                    RSS
                  </span>
                </a>
                
              </li>
            
          </ul>
        </nav>
      </div>
    </div>
  </div>
</header>

      <link rel="stylesheet" type="text/css" href="/css/sharejs.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.css">
<div class="pattern-center-blank"></div>

  <div class="pattern-center single-center">
    <!-- 有配图默认渲染第一张 -->
    <div class="pattern-attachment-img lazyload" style="background-image: url(https://images.unsplash.com/photo-1547122891-8aae3f2a03dc?w=540);" src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/loader/orange.progress-bar-stripe-loader.svg" data-src="https://images.unsplash.com/photo-1547122891-8aae3f2a03dc?w=540">
    </div>
    <header class="pattern-header single-header">
      <h1 class="entry-title">
      RabbitMQ如何保证消息的可靠性传输</h1>
      <p class="entry-census">
        <span>
          <a href="https://blog.xiongtianci.com">
            <img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/avatar.jpg">
          </a>
        </span>
        <span>
          <a href="https://blog.xiongtianci.com">木浅</a>
        </span>
        <span class="bull">
        ·</span>
        2025-3-24<span class="bull">
        ·</span>
      <span id="busuanzi_value_page_pv"></span>次阅读</p>
    </header>
  </div>

<div id="content" class="site-content">
  <div id="primary" class="content-area">
    <main id="main" class="site-main" role="main">
      <article id="post-1" class="post-1 post type-post status-publish format-standard has-post-thumbnail hentry category-uncategorized">
        <div class="toc"></div>
        <!--<div class="toc-entry-content"><!-- 套嵌目录使用（主要为了支援评论）-->
        
        <div class="entry-content">
          <h1 id="消息丢失的场景"><a href="#消息丢失的场景" class="headerlink" title="消息丢失的场景"></a>消息丢失的场景</h1><p>RabbitMQ丢失消息的3种情况：</p>
<p><img src="//cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@28b654d/blog/20250324/image-20250324142239134.png" alt="image-20250324142239134"></p>
<ol>
<li>生产者丢失消息<ul>
<li>场景： 生产者发送消息后，RabbitMQ 服务器崩溃或网络中断，导致消息未到达 RabbitMQ。</li>
<li>解决方案： 使用 Confirm 模式，确保消息成功到达 RabbitMQ</li>
</ul>
</li>
<li><p>RabbitMQ 服务器丢失消息</p>
<ul>
<li>场景： RabbitMQ 服务器崩溃，且消息未持久化。</li>
<li>解决方案： 将队列和消息都设置为持久化（<code>durable=true</code> 和 <code>deliveryMode=2</code>）。</li>
</ul>
</li>
<li><p>消费者丢失消息</p>
<ul>
<li>场景： 消费者处理消息后，未发送 ACK，且消费者崩溃，导致 RabbitMQ 认为消息未被处理。</li>
<li>解决方案<strong>：</strong> 使用 手动 ACK，确保消息处理成功后再发送 ACK。</li>
</ul>
</li>
<li>消息过期<ul>
<li>场景： 消息设置了 TTL（Time-To-Live），在过期后会被自动删除。</li>
<li>解决方案： 合理设置消息的 TTL，或监控过期消息。</li>
</ul>
</li>
<li>队列溢出<ul>
<li>场景： 队列达到最大长度限制，新消息会被丢弃。</li>
<li>解决方案： 监控队列长度，合理设置队列的最大长度。</li>
</ul>
</li>
</ol>
<p>RabbitMQ 提供了以下机制来确保消息传输的可靠性：</p>
<ol>
<li><p>消息持久化</p>
<ul>
<li><p>队列持久化： 将队列设置为持久化（<code>durable=true</code>），即使 RabbitMQ 服务器重启，队列也不会丢失。</p>
</li>
<li><p>消息持久化<strong>：</strong> 将消息设置为持久化（<code>deliveryMode=2</code>），即使 RabbitMQ 服务器重启，消息也不会丢失。</p>
</li>
</ul>
</li>
<li><p>生产者确认机制（Publisher Confirm）</p>
<ul>
<li><p>Confirm 模式： 生产者发送消息后，RabbitMQ 会返回一个确认（<code>ack</code>），表示消息已经成功到达 RabbitMQ 服务器。</p>
</li>
<li><p>Return 模式： 如果消息无法路由到队列，RabbitMQ 会返回一个 <code>return</code>，生产者可以处理这种异常情况。</p>
</li>
</ul>
</li>
<li><p>消费者确认机制（Consumer Ack）</p>
<ul>
<li><p>手动 ACK： 消费者处理完消息后，手动发送一个确认（<code>ack</code>）给 RabbitMQ，RabbitMQ 才会将消息从队列中移除。</p>
</li>
<li><p>NACK 或 Reject： 如果消费者处理消息失败，可以发送 <code>nack</code> 或 <code>reject</code>，RabbitMQ 会将消息重新入队或丢弃。</p>
</li>
</ul>
</li>
<li><p>事务机制</p>
<ul>
<li>事务模式： 生产者可以使用事务机制确保消息的原子性，但性能较差，通常不推荐使用。</li>
</ul>
</li>
</ol>
<h1 id="如何确保消息不丢失"><a href="#如何确保消息不丢失" class="headerlink" title="如何确保消息不丢失"></a>如何确保消息不丢失</h1><p><img src="//cdn.jsdelivr.net/gh/tiancixiong/BlogIMG@28b654d/blog/20250324/image-20250324143814782.png" alt="image-20250324143814782"></p>
<p>为了确保消息不丢失，可以采取以下措施：</p>
<ol>
<li><p>生产者端</p>
<ul>
<li><p>使用 Confirm 模式，确保消息成功到达 RabbitMQ。</p>
</li>
<li><p>实现消息重试机制，在消息发送失败时重试。</p>
</li>
</ul>
</li>
<li><p>RabbitMQ 端</p>
<ul>
<li><p>将队列和消息都设置为持久化。</p>
</li>
<li><p>使用镜像队列（Mirrored Queues），确保消息在多个节点上备份。</p>
</li>
</ul>
</li>
<li><p>消费者端</p>
<ul>
<li><p>使用 手动 ACK，确保消息处理成功后再发送 ACK。</p>
</li>
<li><p>实现消息重试机制，在消息处理失败时重试。</p>
</li>
</ul>
</li>
<li><p>监控和报警</p>
<ul>
<li><p>监控 RabbitMQ 的状态，包括队列长度、消息堆积情况等。</p>
</li>
<li><p>设置报警机制，及时发现和处理异常情况。</p>
</li>
</ul>
</li>
</ol>
<h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><p>parent：<code>org.springframework.boot:spring-boot-starter-parent:2.7.10</code></p>
<pre><code class="xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
    &lt;artifactId&gt;lombok&lt;/artifactId&gt;
    &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
</code></pre>
<p>config：</p>
<pre><code class="java">import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    // 定义队列
    @Bean
    public Queue myQueue() {
        return QueueBuilder.durable(&quot;myQueue&quot;) // 持久化队列
                // 将处理失败的消息转移到死信队列，避免阻塞正常队列
                .withArgument(&quot;x-dead-letter-exchange&quot;, &quot;myDLX&quot;) // 绑定死信交换机
                .withArgument(&quot;x-dead-letter-routing-key&quot;, &quot;myDLRoutingKey&quot;) // 绑定死信路由键
                .build();
    }

    // 定义交换机
    @Bean
    public DirectExchange myExchange() {
        return new DirectExchange(&quot;myExchange&quot;, true, false);
    }

    // 绑定队列和交换机
    @Bean
    public Binding binding(Queue myQueue, DirectExchange myExchange) {
        return BindingBuilder.bind(myQueue)
                .to(myExchange)
                .with(&quot;myRoutingKey&quot;);
    }

    // 定义死信队列
    @Bean
    public Queue myDLQueue() {
        return QueueBuilder.durable(&quot;myDLQueue&quot;) // 持久化死信队列
                .build();
    }

    // 定义死信交换机
    @Bean
    public DirectExchange myDLX() {
        return ExchangeBuilder.directExchange(&quot;myDLX&quot;)
                .durable(true) // 持久化死信交换机
                .build();
    }

    // 绑定死信队列和死信交换机
    @Bean
    public Binding dlBinding(Queue myDLQueue, DirectExchange myDLX) {
        return BindingBuilder.bind(myDLQueue)
                .to(myDLX)
                .with(&quot;myDLRoutingKey&quot;);
    }
}
</code></pre>
<blockquote>
<p>死信队列（Dead Letter Queue, DLQ）：是 RabbitMQ 的一种容错机制，用于接收无法被正常消费的消息（即“死信”）。可以将无法处理的消息从主队列分离，避免阻塞正常业务。</p>
<ul>
<li>持久化机制：默认继承原消息属性。原始消息设置了 deliveryMode=2（持久化），则死信消息自动持久化到磁盘。</li>
<li>超时机制（TTL）：死信消息本身没有默认TTL，除非显式设置，否则死信消息会永久保留在队列中。</li>
</ul>
</blockquote>
<p>配置 confirm 回调和 return 回调：</p>
<pre><code class="java">import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@Slf4j
public class RabbitMQTemplateConfig {

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 开启强制标志（确保消息路由失败时能触发ReturnCallback）
        rabbitTemplate.setMandatory(true);
        // 设置Confirm回调
        rabbitTemplate.setConfirmCallback(confirmCallback());
        // 设置Return回调（新版本推荐使用setReturnsCallback）
        rabbitTemplate.setReturnsCallback(returnsCallback());
        return rabbitTemplate;
    }

    // Confirm 回调
    private RabbitTemplate.ConfirmCallback confirmCallback() {
        return (correlationData, ack, cause) -&gt; {
            if (correlationData == null) {
                log.warn(&quot;Confirm回调收到null correlationData&quot;);
                return;
            }

            if (ack) {
                log.info(&quot;消息发送成功: [ID={}]&quot;, correlationData.getId());
            } else {
                log.error(&quot;消息发送失败: [ID={}] [原因:{}]&quot;, correlationData.getId(), cause);
                // 这里可以添加失败处理逻辑，如记录到数据库等待重试
            }
        };
    }

    // Return 回调处理路由失败（当消息无法路由到队列时触发）
    private RabbitTemplate.ReturnsCallback returnsCallback() {
        return returned -&gt; {
            Message message = returned.getMessage();
            MessageProperties properties = message.getMessageProperties();

            log.error(&quot;消息无法路由到队列: [Body={}] [原因:{}] [Exchange={}] [RoutingKey={}] [Headers={}]&quot;,
                    new String(message.getBody()),
                    returned.getReplyText(),
                    returned.getExchange(),
                    returned.getRoutingKey(),
                    properties.getHeaders());

            // 这里可以添加路由失败处理逻辑，如记录到数据库等待人工干预
        };
    }
}
</code></pre>
<p>生产者：</p>
<pre><code class="java">import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Service
public class MessageProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(String message) {
        // 1. 构建消息属性（设置消息持久化）
        MessageProperties properties = new MessageProperties();
        properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);

        // 2. 设置消息头（包括重试次数和唯一 correlationId）
        Map&lt;String, Object&gt; headers = new HashMap&lt;&gt;();
        // 添加 retryCount 头信息，初始值为 0
        headers.put(&quot;retryCount&quot;, 0);
        String correlationId = UUID.randomUUID().toString(); // 全局唯一ID
        headers.put(&quot;correlationId&quot;, correlationId);
        headers.put(&quot;traceId&quot;, UUID.randomUUID().toString()); // 用于全链路追踪
        properties.setHeaders(headers);

        // 3. 创建消息和 CorrelationData
        Message msg = new Message(message.getBytes(), properties);
        CorrelationData correlationData = new CorrelationData(correlationId);

        // 4. 发送消息（携带 CorrelationData）
        rabbitTemplate.convertAndSend(&quot;myExchange&quot;, &quot;myRoutingKey&quot;, msg, correlationData);

        log.debug(&quot;消息已发送: [ID={}] [TraceID={}]&quot;, correlationId, headers.get(&quot;traceId&quot;));
    }
}
</code></pre>
<blockquote>
<p>CorrelationData 是 Spring AMQP 提供的一个消息关联标识对象，主要用于 Publisher Confirm 模式下关联发送的消息和确认结果。通过 <code>id</code> 字段（通常是业务ID或UUID）唯一标识消息。</p>
<p>几个ID：</p>
<ul>
<li>correlationId：关联ID。用于生产者确认（Publisher Confirm）模式，关联发送的消息和服务器确认回执。在需要幂等性处理的场景中，用于识别重复消息。</li>
<li>traceId：追踪ID。用于全链路追踪，跨服务、跨系统的调用链追踪；在分布式系统中，通过TraceId将不同服务的日志串联起来。</li>
</ul>
</blockquote>
<p>消费者：</p>
<pre><code class="java">import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class MessageConsumer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    private static final int MAX_RETRY_COUNT = 3; // 最大重试次数

    @RabbitListener(queues = &quot;myQueue&quot;)
    public void handleMessage(Message message, Channel channel) throws IOException {
        try {
            // 1. 解析消息
            String msgBody = new String(message.getBody());
            Map&lt;String, Object&gt; headers = message.getMessageProperties().getHeaders();
            log.info(&quot;收到消息: [Body={}] [TraceID={}]&quot;,
                    msgBody, headers.get(&quot;traceId&quot;));

            // 2. 模拟业务处理失败
            if (msgBody.contains(&quot;error&quot;)) {
                throw new RuntimeException(&quot;模拟业务处理失败&quot;);
            }

            // 3. 处理成功，手动ACK
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 4. 处理失败逻辑
            handleRetry(message, channel, e);
        }
    }

    private void handleRetry(Message message, Channel channel, Exception e) throws IOException {
        MessageProperties originalProps = message.getMessageProperties();
        Map&lt;String, Object&gt; headers = originalProps.getHeaders();
        // 获取消息头中的重试次数
        int retryCount = (int) headers.getOrDefault(&quot;retryCount&quot;, 0);

        if (retryCount &gt;= MAX_RETRY_COUNT) {
            // 超过最大重试次数，转入死信队列
            channel.basicNack(originalProps.getDeliveryTag(), false, false);
            log.error(&quot;消息处理失败，已达到最大重试次数，转移到死信队列: [TraceID={}]&quot;, headers.get(&quot;traceId&quot;));
        } else {
            // 未超最大重试次数，重新入队
            headers.put(&quot;retryCount&quot;, retryCount + 1); // 更新重试次数
            // 创建新消息属性
            MessageProperties newProps = new MessageProperties();
            // 1) 拷贝持久化设置
            MessageDeliveryMode receivedMode = originalProps.getReceivedDeliveryMode();
            if (receivedMode != null) {
                newProps.setDeliveryMode(receivedMode);
            } else {
                // 默认设置为持久化（安全策略）
                newProps.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            }
            // 2) 拷贝其他必要属性
            newProps.setHeaders(new HashMap&lt;&gt;(originalProps.getHeaders())); // 保留原始 correlationId 和 traceId
            newProps.setContentType(originalProps.getContentType());
            newProps.setContentEncoding(originalProps.getContentEncoding());
            newProps.setCorrelationId(originalProps.getCorrelationId());
            newProps.setReplyTo(originalProps.getReplyTo());

            // 发布新消息到队列（必须传递 CorrelationData）
            rabbitTemplate.convertAndSend(
                    &quot;myExchange&quot;,
                    &quot;myRoutingKey&quot;,
                    new Message(message.getBody(), newProps),
                    new CorrelationData(headers.get(&quot;correlationId&quot;).toString()) // 防止生产者 Confirm 回调出现 NPE
            );

            // 确认原消息已处理（即使失败）
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            log.warn(&quot;消息重试中: [次数={}] [TraceID={}]&quot;,
                    retryCount + 1,
                    headers.get(&quot;traceId&quot;));
        }
    }

    // 指数退避算法
    private long calculateBackoffDelay(int retryCount) {
        long baseDelay = 5000; // 基础延迟5秒
        long maxDelay = 60000; // 最大延迟60秒
        return Math.min(baseDelay * (1 &lt;&lt; retryCount), maxDelay);
    }

    // 增加死信处理
    @RabbitListener(queues = &quot;myDLQueue&quot;)
    public void handleDeadLetter(Message message, Channel channel) throws IOException {
        try {
            String msg = new String(message.getBody());
            log.error(&quot;收到死信消息: {}&quot;, msg);

            // 可选操作：
            // 1. 记录到数据库（如MySQL、Elasticsearch）
            // 2. 发送告警（邮件/短信/钉钉）
            // 3. 人工介入或自动修复

            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            log.info(&quot;死信消息已确认处理完成&quot;);
        } catch (Exception e) {
            // 处理失败时拒绝消息（避免死循环）
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            log.error(&quot;死信消息处理失败: {}&quot;, e.getMessage());
        }
    }
}
</code></pre>
<blockquote>
<ul>
<li>CorrelationData 传递：重试时从消息 Header 中读取原始 <code>correlationId</code> 并重建</li>
<li>消息追踪：生产者在消息头中添加追踪ID（traceId）字段，通过唯一ID串联消息经过的所有服务（生产者→MQ→消费者→其他服务），以支持完整的消息生命周期追踪。消费者重试逻辑中需要保持原始 traceId。</li>
<li>消费者重试机制的逻辑是 <code>重建新消息+ACK原消息</code> 的设计，主要是因为如果直接 <code>basicNack(requeue=true)</code> 会导致消息头（如 <code>retryCount</code> ）无法更新。</li>
<li><code>originalProps.getReceivedDeliveryMode()</code></li>
</ul>
</blockquote>
<p>controller：</p>
<pre><code class="java">@RestController
public class TestController {
    @Autowired
    private MessageProducer messageProducer;
    @GetMapping(&quot;/send&quot;)
    public ResponseEntity&lt;String&gt; sendMessage(@RequestParam String message) {
        messageProducer.sendMessage(message);
        return new ResponseEntity&lt;&gt;(&quot;消息已发送: &quot; + message, HttpStatus.OK);
    }
}
</code></pre>
<p>配置信息：</p>
<pre><code class="yml">spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    # 开启生产者确认模式
    # NONE-禁用 Confirm 模式
    # SIMPLE-启用 Confirm 模式，并同步等待确认结果
    # CORRELATED-启用 Confirm 模式，并异步处理确认结果
    publisher-confirm-type: correlated
    # 开启生产者返回模式
    publisher-returns: true
    # 开启消费者手动 ACK
    listener:
      simple:
        acknowledge-mode: manual
        retry:
          enabled: false # 禁用Spring的自动重试，使用我们自定义的重试逻辑
</code></pre>
<p>启动类：</p>
<pre><code class="java">@SpringBootApplication
@EnableRabbit // 启用 RabbitMQ 监听功能
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
</code></pre>
<p><strong>测试</strong></p>
<ul>
<li>访问 <code>http://localhost:8080/send?message=Hello RabbitMQ</code>，发送正常消息</li>
<li>访问 <code>http://localhost:8080/send?message=error</code>，发送会失败的消息</li>
</ul>
<p>失败的消息大致生命周期：</p>
<pre><code class="mermaid">sequenceDiagram
    participant Broker
    participant Consumer

    Broker-&gt;&gt;Consumer: 消息A(retryCount=0)
    Consumer-&gt;&gt;Broker: ACK消息A + 发送消息B(retryCount=1)
    Broker-&gt;&gt;Consumer: 消息B(retryCount=1)
    Consumer-&gt;&gt;Broker: ACK消息B + 发送消息C(retryCount=2)
    Broker-&gt;&gt;Consumer: 消息C(retryCount=2)
    Consumer-&gt;&gt;Broker: NACK消息C → 转入死信队列
</code></pre>
<h1 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h1><h2 id="优化重试机制"><a href="#优化重试机制" class="headerlink" title="优化重试机制"></a>优化重试机制</h2><p>消费者重试<strong>不建议立即重试</strong>，而是应该采用<strong>延迟重试策略</strong>。很多情况下立即重试仍会失败，浪费资源；消息持续失败导致高频重试，CPU/内存飙升，拖垮整个服务。</p>
<p>立即重试的问题分析：</p>
<table>
<thead>
<tr>
<th style="text-align:left">问题</th>
<th style="text-align:left">风险</th>
<th style="text-align:left">解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>资源风暴</strong></td>
<td style="text-align:left">消息处理失败后立即重试，可能引发瞬时高负载</td>
<td style="text-align:left">引入指数退避延迟</td>
</tr>
<tr>
<td style="text-align:left"><strong>无效重试</strong></td>
<td style="text-align:left">若失败原因是下游服务短暂不可用，立即重试大概率再次失败</td>
<td style="text-align:left">逐步增加重试间隔</td>
</tr>
<tr>
<td style="text-align:left"><strong>消息堆积</strong></td>
<td style="text-align:left">高频重试会占用队列资源，阻塞其他消息处理</td>
<td style="text-align:left">使用独立延迟队列</td>
</tr>
</tbody>
</table>
<p>常见的重试策略：</p>
<ol>
<li><strong>指数退避重试 (Exponential Backoff)</strong><ul>
<li>重试间隔随时间指数增长（如1s, 2s, 4s, 8s…）</li>
<li>防止短时间内大量重试导致系统雪崩</li>
</ul>
</li>
<li><strong>固定间隔重试</strong><ul>
<li>每次重试之间保持固定时间间隔</li>
<li>比立即重试更温和，但不如指数退避灵活</li>
</ul>
</li>
<li><strong>死信队列(DLX) + 延迟队列</strong><ul>
<li>处理失败的消息先进入死信队列</li>
<li>通过TTL或插件实现延迟重试</li>
<li>这是RabbitMQ中最常见的生产级方案</li>
</ul>
</li>
</ol>
<p>接下来基于指数退避重试机制，结合延迟交换机（Delayed Exchange）优化重试机制。</p>
<p>消息流转大致流程：</p>
<pre><code class="mermaid">sequenceDiagram
    participant Producer
    participant MainQueue
    participant Consumer
    participant RetryQueue
    participant DLQ

    Producer-&gt;&gt;MainQueue: 发送消息
    Consumer-&gt;&gt;MainQueue: 消费消息
    alt 处理成功
        Consumer-&gt;&gt;MainQueue: ACK
    else 处理失败且可重试
        Consumer-&gt;&gt;RetryQueue: 转发消息(带延迟)
        RetryQueue--&gt;&gt;MainQueue: TTL到期后重新投递
    else 彻底失败
        Consumer-&gt;&gt;DLQ: NACK转入死信
    end
</code></pre>
<p>需安装 <code>rabbitmq_delayed_message_exchange</code> 插件，声明 <code>x-delayed-message</code> 类型交换机。RabbitMQ官方插件支持每条消息独立延迟：</p>
<pre><code class="bash"># linux
# 进入RabbitMQ容器执行
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
# 验证插件列表应包含
rabbitmq-plugins list | grep delay

# windows
rabbitmq_server-3.7.14\sbin&gt;rabbitmq-plugins.bat enable rabbitmq_delayed_message_exchange
rabbitmq_server-3.7.14\sbin&gt;rabbitmq-plugins.bat list | find &quot;delay&quot;
</code></pre>
<p>修正RabbitMQ配置类（确保交换机、队列、绑定关系一致）</p>
<pre><code class="java">@Configuration
public class RabbitMQConfig {
    // 主业务队列（保持不变）
    @Bean public Queue myQueue() { /* ... */ }
    @Bean public DirectExchange myExchange() { /* ... */ }
    @Bean public Binding binding() { /* ... */ }
    // 死信队列和交换机（保持不变）
    @Bean public Queue myDLQueue() { /* ... */ }
    @Bean public DirectExchange myDLX() { /* ... */ }
    @Bean public Binding dlBinding() { /* ... */ }

    // 延迟交换机（插件支持）（新增）
    @Bean
    public CustomExchange delayExchange() {
        Map&lt;String, Object&gt; args = new HashMap&lt;&gt;();
        args.put(&quot;x-delayed-type&quot;, &quot;direct&quot;); // 底层仍用direct路由
        return new CustomExchange(&quot;myDelayExchange&quot;, &quot;x-delayed-message&quot;, true, false, args);
    }

    // 绑定延迟交换机到延迟队列（新增）
    @Bean
    public Binding delayBinding(Queue myQueue, CustomExchange delayExchange) {
        return BindingBuilder.bind(myQueue)
                .to(delayExchange)
                .with(&quot;myRoutingKey&quot;) // 和主队列的 routingKey 一致
                .noargs();
    }
}
</code></pre>
<p>延迟消息不会 <strong>立即</strong> 进入队列，而是由 <strong><code>x-delayed-message</code></strong> 插件 <strong>暂存</strong>，直到延迟时间到期后才投递，会导致 <code>ReturnCallback</code> 可能在消息被暂存时误触发。所以需要修改 <code>RabbitMQTemplateConfig#returnsCallback()</code> 方法：</p>
<pre><code class="java">private RabbitTemplate.ReturnsCallback returnsCallback() {
    return returned -&gt; {
        // 如果是延迟交换机，忽略 ReturnCallback
        if (&quot;myDelayExchange&quot;.equals(returned.getExchange())) {
            log.debug(&quot;延迟消息暂存，忽略 ReturnCallback&quot;);
            return;
        }

        Message message = returned.getMessage();
        MessageProperties properties = message.getMessageProperties();

        log.error(&quot;消息无法路由到队列: [Body={}] [原因:{}] [Exchange={}] [RoutingKey={}] [Headers={}]&quot;,
                new String(message.getBody()),
                returned.getReplyText(),
                returned.getExchange(),
                returned.getRoutingKey(),
                properties.getHeaders());

        // 这里可以添加路由失败处理逻辑，如记录到数据库等待人工干预
    };
}
</code></pre>
<p>生产者无需修改，消费者需要修改 <code>handleRetry()</code> 方法，将重试消息发布到延迟交换机而不是直接重发给主队列：</p>
<pre><code class="java">private void handleRetry(Message message, Channel channel, Exception e) throws IOException {
    // ... 原有业务逻辑 ...

    if (retryCount &gt;= MAX_RETRY_COUNT) {
        // ... 原有业务逻辑 ...
    } else {
        // ... 原有业务逻辑 ...

        // 计算动态延迟（指数退避：5s, 10s, 20s...）
        long delayMs = calculateBackoffDelay(retryCount);

        // 设置重试相关属性
        newProps.setHeader(&quot;x-delay&quot;, delayMs); // 添加延迟头
        newProps.setHeader(&quot;retryCount&quot;, retryCount + 1); // 更新重试次数

        // 发布到延迟交换机
        rabbitTemplate.convertAndSend(
                &quot;myDelayExchange&quot;, // 使用延迟交换机
                &quot;myRoutingKey&quot;, // 使用主队列的 routingKey
                new Message(message.getBody(), newProps),
                m -&gt; {
                    // 设置消息级延迟（单位：毫秒）
                    m.getMessageProperties().setHeader(&quot;x-delay&quot;, delayMs);
                    return m;
                },
                new CorrelationData(headers.get(&quot;correlationId&quot;).toString())
        );

        // 确认原消息
        channel.basicAck(originalProps.getDeliveryTag(), false);
        log.warn(&quot;消息将在 {}ms 后重试 (第{}次)&quot;, delayMs, retryCount + 1);
    }
}
</code></pre>
<pre><code class="java">// 指数退避算法
private long calculateBackoffDelay(int retryCount) {
    long baseDelay = 5000; // 基础延迟5秒
    long maxDelay = 60000; // 最大延迟60秒
    return Math.min(baseDelay * (1 &lt;&lt; retryCount), maxDelay);
}
</code></pre>
<h1 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h1><h2 id="RabbitMQ命令"><a href="#RabbitMQ命令" class="headerlink" title="RabbitMQ命令"></a>RabbitMQ命令</h2><p><strong>查看持久化配置</strong></p>
<p>消息持久化需要三个条件同时满足：</p>
<ul>
<li>交换机持久化</li>
<li>队列持久化</li>
<li>消息发布时设置 <code>properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);</code></li>
</ul>
<pre><code class="bash"># 1、列出所有交换机及其属性
rabbitmqctl list_exchanges name type durable auto_delete arguments
# durable 列为 true 表示交换机设置了持久化

# 2、列出所有队列及其属性
rabbitmqctl list_queues name durable auto_delete arguments
# durable 列为 true 表示队列设置了持久化

# 3、查看消息持久化 (取决于发布时设置的delivery_mode：1-非持久化、2-持久化)
# 获取队列中的消息详情：properties.delivery_mode (需要安装rabbitmq_management插件)
# linux
rabbitmqadmin get queue=&lt;queue_name&gt; count=1
# windows
python rabbitmqadmin get queue=myQueue count=1 --format=raw_json
</code></pre>
<p><strong>绑定关系验证</strong>：</p>
<pre><code class="bash"># 列出所有交换机
rabbitmqadmin list exchanges name type arguments
# 列出队列
rabbitmqadmin list queues name messages
# 列出绑定关系
rabbitmqadmin list bindings
</code></pre>
<blockquote>
<p>windows 需要从 <code>http://localhost:15672/cli/</code> 下载 <code>rabbitmqadmin</code>，并且用 <code>python rabbitmqadmin ...</code> 运行命令。</p>
</blockquote>
<h2 id="消费者重试时丢失了deliveryMode-2"><a href="#消费者重试时丢失了deliveryMode-2" class="headerlink" title="消费者重试时丢失了deliveryMode=2"></a>消费者重试时丢失了<code>deliveryMode=2</code></h2><p>在上面代码中，重试机制里面有段代码：</p>
<pre><code class="java">// 1) 拷贝持久化设置
MessageDeliveryMode receivedMode = originalProps.getReceivedDeliveryMode();
if (receivedMode != null) {
    newProps.setDeliveryMode(receivedMode);
} else {
    // 默认设置为持久化（安全策略）
    newProps.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
}
</code></pre>
<p>这里如果只是通过 <code>message.getMessageProperties()</code> 获取原消息属性 <code>originalProps</code> 直接设置给新消息属性 <code>newProps</code>，这样子就会导致新消息会丢失 <code>deliveryMode=2</code> （持久化）配置信息，最后导致消息不会进行持久化了。</p>
<p>原因：</p>
<ul>
<li>当生产者发送消息时，<code>deliveryMode=2</code>（持久化）是 AMQP 协议层的属性，保存在消息的 <code>BasicProperties</code> 中。</li>
<li>Spring AMQP 在 <code>@RabbitListener</code> 中会将原始 AMQP 消息转换为 <code>Message</code> 对象时，<strong>会丢失部分协议层属性</strong>（包括 <code>deliveryMode</code>）。</li>
</ul>
<p>在 Spring AMQP 2.3 及以上版本中，可以直接通过 <code>MessageProperties</code> 的 <code>getReceivedDeliveryMode()</code> 方法获取生产者设置的原始 <code>deliveryMode</code> 值。</p>

        </div>
        <!-- .entry-content -->
        <div class="single-reward">
          <div class="reward-open">赏<div class="reward-main">
              <ul class="reward-row">
                <li class="alipay-code"><img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/donate/AliPayQR.jpg"></li>
                <li class="wechat-code"><img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/donate/WeChanQR.png"></li>
              </ul>
            </div>
          </div>
        </div>
        <div style="text-align:center; width: 100%" class="social-share share-mobile" data-disabled="diandian, tencent"></div>
        <footer class="post-footer">
          <div class="post-lincenses"><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank" rel="nofollow"><i class="fa fa-creative-commons" aria-hidden="true"></i> 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a></div>
          <div class="post-tags">
          </div>
          <div class="post-share">
            <div class="social-share sharehidden share-component"></div>
            <i class="iconfont show-share icon-forward"></i>
          </div>
        </footer><!-- .entry-footer -->
      </article>
      <!-- #post-## -->
      <!--<div class="toc" style="background: none;"></div>-->
      <section class="post-squares nextprev">
        
        
          
            <div class="post-nepre full next">
          
            <a href="/2025/03/23/加密存储在数据库中的数据如何进行模糊查询/" rel="next">
              <div class="background">
                <img class="lazyload" src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/loader/orange.progress-bar-stripe-loader.svg" data-src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=540" style="width: 100%; height: 100%; object-fit: cover; pointer-events: none;" onerror="imgError(this,3)" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=540">
              </div>
              <span class="label">
              Next Post</span>
              <div class="info">
                <h3>
                加密存储在数据库中的数据如何进行模糊查询</h3>
                <hr>
              </div>
            </a>
          </div>
        
      </section>
      <!-- 评论插件valine -->

<div id="vcomments"></div>
<script>
  window.onload = function(){
      var valine = new Valine();
      valine.init({
        el: '#vcomments',
        appId: "VeenC2gYYyfSOq2rcCSmmruy-MdYXbMMI",
        appKey: "F5kjyHdh4akqc7SDmfzQP8Qi",
		notify: false,
		verify: false,
        path: window.location.pathname,
        placeholder: "你是我一生只会遇见一次的惊喜 ...",
		avatar: "monsterid",
		//meta: [nick,mail,link],
		visitor: true,
        recordIP: "true",
		pageSize: "5" || 10,
		serverURLs: "https://VeenC2gY.api.lncldglobal.com"
      });
	//增加以下六行代码去除 power by valine
    var infoEle = document.querySelector('#vcomments .info');
    if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0){
      infoEle.childNodes.forEach(function(item) {
        item.parentNode.removeChild(item);
      });
    }
  }
</script>

      <section class="author-profile">
        <div class="info" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
          <a href="https://blog.xiongtianci.com" class="profile gravatar"><img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/avatar.jpg" itemprop="image" alt="木浅" height="70" width="70"></a>
          <div class="meta">
            <span class="title">Author</span>
            <h3 itemprop="name">
            <a href="https://blog.xiongtianci.com" itemprop="url" rel="author">木浅</a>
            </h3>
          </div>
        </div>
        <hr>
        <p><i class="iconfont icon-write"></i>YOLO</p>
      </section>
    </main><!-- #main -->
  </div><!-- #primary -->
</div>


    </div>    
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="Type something..."/>
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: 'Posts',
            // PAGES: 'Pages',
            CATEGORIES: 'Categories',
            TAGS: 'Tags',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
    <!-- <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2025 天赐<br>
      powered_by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer> -->
<footer id="colophon" class="site-footer" role="contentinfo">
	<div class="site-info">
		<div class="footertext">
			<div class="img-preload">
				<img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@1.0/img/other/wordpress-rotating-ball-o.svg">
				<img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@1.0/img/other/disqus-preloader.svg">
			</div>
			<p class="foo-logo"
				style="background-image: url('https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/img/custom/sakura.svg');">
			</p>
			<div
				style="background-image:linear-gradient(90deg,#6dba82 0,#07b39b 15%,#1098ad 30%,#5073b8 44%,#a166ab 58%,#ef4e7b 72%,#f37055 86%,#f79533 100%);background-size:cover;-webkit-background-clip:text;-webkit-text-fill-color:transparent;user-select:none;margin-top:0.7em;">
				<i>Tip: 本站电脑访问体验更佳耶&nbsp;<i class="fa fa-angellist" style="display:inline"></i></i>
			</div>
			<!-- color: #666666; -->
			<div style="color: #b9b9b9;">&copy;
				2018-2025&nbsp;天赐&nbsp;
			</div>
		</div>
		<!-- 访客统计 -->
		<!--<div id="busuanzi_container"></div>-->
		<!-- 已运行的时间-容器 -->
		<div id="days"></div>
		<div class="footer-device">
			<p id="footer-sponsor">
				<!-- <a href="https://pages.github.com/" target="_blank" alt="GitHub Pages" rel="nofollow"><img
						src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@1.0/img/logo/GitHub.svg" alt="GitHub Pages"
						style="height: 2em; max-height: 2em;" title=""></a>&nbsp;&nbsp;&nbsp; -->
				<!-- <a href="https://coding.net/pages" target="_blank" alt="Coding Pages" rel="nofollow"><img
						src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@1.0/img/logo/Coding.png" alt="Coding Pages"
						style="height: 1.3em; max-height: 1.3em;padding-bottom: 0px;margin-bottom: 0.35em;"
						title=""></a>&nbsp;&nbsp;&nbsp; -->
				<a href="https://www.jsdelivr.com/" target="_blank" alt="Free CDN" rel="nofollow"><img
						src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/img/logo/JsDelivr_Logo.svg" alt="jsDelivr"
						style="height: 2em; max-height: 2em;padding-bottom: 0px;"></a>&nbsp;&nbsp;&nbsp;<a
					href="https://www.google.com/intl/ja/analytics/" target="_blank" alt="Analytics solution"
					rel="nofollow"><img src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/img/logo/Google.svg"
						alt="Google Analytics" style="height: 1.7em; max-height: 1.7em;padding-bottom: 0px;"></a>
			</p>
		</div>
	</div>
	<!-- .site-info -->
</footer>

<!-- <script src="/js/tocbot.js"></script> -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/lib.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/vendor/clipboard.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/InsightSearch.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/jquery.fancybox.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/zoom.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/sakura-app.js"></script>
<!--浏览器搞笑标题-->
<!--<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/title_change.js"></script>-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/volantis.min.js"></script>
<!-- Valine -->
<!--<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>-->
<script src='//unpkg.com/valine/dist/Valine.min.js'></script>
<!--<script src='//unpkg.com/valine@1.3.4/dist/Valine.min.js'></script>-->
<!--<script src="//unpkg.com/valine@1.3.7/dist/Valine.min.js"></script>-->
<!-- 不蒜子 网页计数器 -->
<script src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.min.js"></script> -->
<script type="text/javascript">
	/* <![CDATA[ */
	if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
		var Poi = {
			"pjax": "1",
			"movies": {
				"url": "https://cdn.jsdelivr.net/gh/tiancixiong/cdn@v3.0",
				"name": "/video/Unbroken.mp4",
				"live": "close"
			},
			"windowheight": "fixed",
			"codelamp": "close",
			"ajaxurl": "",
			"order": "asc",
			"formpostion": "bottom"
		};
	} else {
		var Poi = {
			"pjax": "1",
			"movies": {
				"url": "https://cdn.jsdelivr.net/gh/tiancixiong/cdn@v3.0",
				"name": "/video/Unbroken.mp4",
				"live": "open"
			},
			"windowheight": "auto",
			"codelamp": "close",
			"ajaxurl": "",
			"order": "asc",
			"formpostion": "bottom"
		};
	}
		/* ]]> */
</script>
<script>
	$(document).ready(function () {
		if ($(".toc").length > 0 && document.body.clientWidth > 1200) {
			if ($(".pattern-center").length > 0) { //有图的情况
				tocbot.init({
					// Where to render the table of contents.
					tocSelector: '.toc', // 放置目录的容器
					// Where to grab the headings to build the table of contents.
					contentSelector: '.entry-content', // 正文内容所在
					// Which headings to grab inside of the contentSelector element.
					scrollSmooth: true,
					headingSelector: 'h1, h2, h3, h4, h5', // 需要索引的标题级别
					headingsOffset: -400,
					scrollSmoothOffset: -85
				});
			} else {
				tocbot.init({
					// Where to render the table of contents.
					tocSelector: '.toc', // 放置目录的容器
					// Where to grab the headings to build the table of contents.
					contentSelector: '.entry-content', // 正文内容所在
					// Which headings to grab inside of the contentSelector element.
					scrollSmooth: true,
					headingSelector: 'h1, h2, h3, h4, h5', // 需要索引的标题级别
					headingsOffset: -85,
					scrollSmoothOffset: -85
				});
			}
			var offsetTop = $('.toc').offset().top - 95;
			window.onscroll = function () {
				var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
				if (scrollTop >= offsetTop) {
					$('.toc').addClass('toc-fixed');
				} else {
					$('.toc').removeClass('toc-fixed');
				}
			}
		}
	});
</script>
<!-- 已运行的时间-js -->
<script>
	function show_date_time() {
		window.setTimeout("show_date_time()", 1000);
		// 获得建站时间
		var time = "28/11/2018 15:36:06";
		time = time.replace(/\//g, ':').replace(' ', ':');
		time = time.split(':');
		BirthDay = new Date(time[2], (time[1] - 1), time[0], time[3], time[4], time[5]);

		today = new Date();
		timeold = (today.getTime() - BirthDay.getTime());
		sectimeold = timeold / 1000
		secondsold = Math.floor(sectimeold);
		msPerDay = 24 * 60 * 60 * 1000
		msPerYear = 365 * 24 * 60 * 60 * 1000
		e_yearsold = timeold / msPerYear
		yearsold = Math.floor(e_yearsold);
		//e_daysold = timeold / msPerDay
		e_daysold = timeold/msPerDay - yearsold*365;
		daysold = Math.floor(e_daysold);
		e_hrsold = (e_daysold - daysold) * 24;
		hrsold = setzero(Math.floor(e_hrsold));
		e_minsold = (e_hrsold - hrsold) * 60;
		minsold = setzero(Math.floor((e_hrsold - hrsold) * 60));
		seconds = setzero(Math.floor((e_minsold - minsold) * 60));
		document.getElementById('days').innerHTML = "已在风雨中运行 " + yearsold + " 年 " + daysold + " 天 " + hrsold + " 小时 " + minsold + " 分 " + seconds + " 秒";
	}

	function setzero(i) {
		if (i < 10) {
			i = "0" + i
		};
		return i;
	}
	show_date_time();
</script>
<!-- 访客统计-js -->
<!--<script>
		if(true){
			document.getElementById('busuanzi_container').innerHTML="<span id=\"busuanzi_container_site_pv\">本站总访问量 <span id=\"busuanzi_value_site_pv\"></span> 次</span><span class=\"post-meta-divider\"> | </span><span id=\"busuanzi_container_site_uv\">本站访客数 <span id=\"busuanzi_value_site_uv\"></span> 人</span>";
		}else{
			document.getElementById('busuanzi_container').hidden;
		}
	</script>-->
<!-- 百度统计自动推送 -->
<!--<script>
	(function(){
		var bp = document.createElement('script');
		var curProtocol = window.location.protocol.split(':')[0];
		if (curProtocol === 'https') {
			bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
		}
		else {
			bp.src = 'http://push.zhanzhang.baidu.com/push.js';
		}
		var scriptList = document.getElementsByTagName("script");
		var s = scriptList[0];
		s.parentNode.insertBefore(bp, s);
	})();
	</script>
	-->
    <div class="openNav no-select" style="height: 50px;">
      <div class="iconflat no-select" style="width: 50px; height: 50px;">
        <div class="icon"></div>
      </div>
      <div class="site-branding search-form-submit">
        <i class="iconfont js-toggle-search iconsearch icon-search"></i>
      </div>
    </div>
  </section>
  
  <!-- 实现换肤功能 -->
  <div class="skin-menu no-select" id="mainskin"  style="position: fixed">
    <div class="theme-controls row-container">
        <ul class="menu-list">
            <li id="white-bg"><i class="fa fa-television" aria-hidden="true"></i></li>
            <li id="sakura-bg"> <i class="iconfont icon-sakura"></i></li>
            <li id="gribs-bg"> <i class="fa fa-slack" aria-hidden="true"></i></li>
            <li id="KAdots-bg"> <i class="iconfont icon-dots"></i></li>
            <li id="totem-bg"> <i class="fa fa-optin-monster" aria-hidden="true"></i></li>
            <li id="pixiv-bg"> <i class="iconfont icon-pixiv"></i></li>
            <li id="bing-bg"> <i class="iconfont icon-bing"></i></li>
            <li id="dark-bg"> <i class="fa fa-moon-o" aria-hidden="true"></i></li>
        </ul>
    </div>
</div>

<canvas id="night-mode-cover"></canvas>
<style>
#mainskin{
    position: fixed;
}
</style> 
  <div class="changeSkin-gear no-select">
    <div class="keys" id="setbtn"> <span id="open-skinMenu"> 切换主题 | SCHEME TOOL &nbsp;
	<i class="iconfont icon-gear inline-block rotating"></i> </span></div>
</div>
  
  <div id="mo-nav" class="">
  <div class="m-avatar">
    <img src="https://fastly.jsdelivr.net/gh/tiancixiong/cdn@v1.0/img/custom/avatar.jpg">
  </div>
  <p style="text-align: center; color: #333; font-weight: 900; font-family: 'Ubuntu', sans-serif; letter-spacing: 1.5px">天赐の小站</p>
  <p style="text-align: center; word-spacing: 20px;">
    
  </p>
  <ul id="menu-new-1" class="menu">
    
      <li>
        <a href="/">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-fort-awesome faa-horizontal" aria-hidden="true"></i>
            首页
          </span>
        </a>
        
      </li>
    
      <li>
        <a href="/archives">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-terminal faa-vertical" aria-hidden="true"></i>
            极客
          </span>
        </a>
        
          <ul class="sub-menu">
            
              <li>
                <a href="/categories/技术/">
                  <i class="fa fa-code" aria-hidden="true"></i>
                  技术
                </a>
              </li>
            
              <li>
                <a href="/categories/Linux/">
                  <i class="fa fa-linux" aria-hidden="true"></i>
                  Linux
                </a>
              </li>
            
          </ul>
        
      </li>
    
      <li>
        <a href="/archives">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-slack faa-spin" aria-hidden="true"></i>
            爱好
          </span>
        </a>
        
          <ul class="sub-menu">
            
              <li>
                <a href="/categories/电影/">
                  <i class="fa fa-film" aria-hidden="true"></i>
                  电影
                </a>
              </li>
            
              <li>
                <a href="/categories/日语/">
                  <i class="fa fa-language" aria-hidden="true"></i>
                  日语
                </a>
              </li>
            
          </ul>
        
      </li>
    
      <li>
        <a href="/archives">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-archive faa-ring" aria-hidden="true"></i>
            归档
          </span>
        </a>
        
          <ul class="sub-menu">
            
              <li>
                <a href="/categories/生活/">
                  <i class="fa fa-file-text-o" aria-hidden="true"></i>
                  生活
                </a>
              </li>
            
              <li>
                <a href="/categories/随想/">
                  <i class="fa fa-commenting-o" aria-hidden="true"></i>
                  随想
                </a>
              </li>
            
              <li>
                <a href="/categories/资源/">
                  <i class="fa fa-cloud-download" aria-hidden="true"></i>
                  资源
                </a>
              </li>
            
              <li>
                <a href="/categories/转载/">
                  <i class="fa fa-book" aria-hidden="true"></i>
                  转载
                </a>
              </li>
            
          </ul>
        
      </li>
    
      <li>
        <a href="/comment/">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-pencil-square-o faa-tada" aria-hidden="true"></i>
            留言板
          </span>
        </a>
        
      </li>
    
      <li>
        <a href="/links/">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-link faa-shake" aria-hidden="true"></i>
            友人帐
          </span>
        </a>
        
      </li>
    
      <li>
        <a href="/donate/">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-heart faa-burst" aria-hidden="true"></i>
            赞赏
          </span>
        </a>
        
      </li>
    
      <li>
        <a href="/">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-leaf faa-falling" aria-hidden="true"></i>
            关于
          </span>
        </a>
        
          <ul class="sub-menu">
            
              <li>
                <a href="/about/">
                  <i class="fa fa-meetup" aria-hidden="true"></i>
                  我？
                </a>
              </li>
            
              <li>
                <a href="/theme-sakura/">
                  <i class="fa iconfont icon-sakura" aria-hidden="true"></i>
                  主题
                </a>
              </li>
            
          </ul>
        
      </li>
    
      <li>
        <a href="/atom.xml">
          <span class="faa-parent animated-hover">
            <i class="fa  fa-rss faa-passing" aria-hidden="true"></i>
            RSS
          </span>
        </a>
        
      </li>
    
  </ul>
  <p style="text-align: center; font-size: 13px; color: #b9b9b9;">&copy 2019 hexo-sakura</p>
</div>
<button onclick="topFunction()" class="mobile-cd-top" id="moblieGoTop" title="Go to top" style="display: none;"><i class="fa fa-chevron-up" aria-hidden="true"></i></button>
  <!--<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.css">-->
<!--<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/css/aplayer/APlayer.min.css">
<script src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/aplayer/APlayer.min.js"></script>

<style>
  .aplayer .aplayer-lrc {
    height: 35px;
  }
  .aplayer .aplayer-lrc p{
    font-size: 16px;
    font-weight: 700;
    line-height: 18px !important;
  }
  .aplayer .aplayer-lrc p.aplayer-lrc-current{
    color: #FF1493;
  }
  /*.aplayer.aplayer-narrow .aplayer-body{
    left: -66px !important;
  }*/
  .aplayer.aplayer-fixed .aplayer-lrc {
    display: none;
  }
  .aplayer .aplayer-lrc.aplayer-lrc-hide {
      display:none !important;
  }
  .aplayer.aplayer-fixed .lrc-show {
    display: block;
    background: rgba(255, 255, 255, 0.8);
  }
</style>
<div class="aplayer"

    data-id="7040682880"

    data-server="tencent"

    data-type="playlist"

    data-fixed="true"

    data-autoplay="false"

    data-loop="all"

    data-order="random"

    data-preload="auto"

    data-volume="0.5"

    data-mutex="true"

    data-listFolded="false"

</div>
<!-- Meting 一个支持各种音乐平台(腾讯、网易...)的音乐 API 框架 -->
<!--<script src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script>-->
<script src="https://cdn.jsdelivr.net/gh/tiancixiong/cdn@sakura-theme/js/aplayer/Meting.min.js"></script>
<script>
  $(function(){
    $('body').on('click', '.aplayer', function(){
      if($('.aplayer-button').hasClass('aplayer-play')) {
        $('.aplayer-lrc').removeClass('lrc-show');
      } else {
        $('.aplayer-lrc').addClass('lrc-show');
      }
    })
  });
</script>
</body>
</html>